using System;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
#if ODIN_INSPECTOR
using Sirenix.OdinInspector;
#endif
using Unity.Cinemachine;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;

namespace JH
{
    /// <summary>
    ///  相机绕物旋转，同时通过移动物体来移动相机
    /// </summary>
    public class AroundWithMoveCamera : MonoBehaviour
    {
        /// <summary>
        ///  虚拟相机Brain
        /// </summary>
        [SerializeField]
#if ODIN_INSPECTOR
        [LabelText("虚拟相机Brain")]
#endif
        private CinemachineBrain brain;

        /// <summary>
        ///  虚拟相机
        /// </summary>
#if ODIN_INSPECTOR
        [LabelText("虚拟相机")]
#endif
        [SerializeField]
        private CinemachineOrbitalFollow orbital;

        /// <summary>
        ///  旋转控制器
        /// </summary>
#if ODIN_INSPECTOR
        [LabelText("旋转控制器")]
#endif
        [SerializeField]
        private CinemachineRotationComposer rotationComposer;

        /// <summary>
        ///  输入资产
        /// </summary>
#if ODIN_INSPECTOR
        [LabelText("输入资产")]
#endif
        [SerializeField]
        private InputActionAsset inputAsset;

#if ODIN_INSPECTOR
        [LabelText("复位")]
#endif
        [SerializeField]
        public InputActionReference reset;

        /// <summary>
        ///  允许移动行为
        /// </summary>
#if ODIN_INSPECTOR
        [LabelText("允许移动行为引用")]
#endif
        [SerializeField]
        private InputActionReference allowMove;

        /// <summary>
        ///  允许旋转行为
        /// </summary>
#if ODIN_INSPECTOR
        [LabelText("允许旋转行为引用")]
#endif
        [SerializeField]
        private InputActionReference allowRotate;

        /// <summary>
        ///  缩放行为
        /// </summary>
#if ODIN_INSPECTOR
        [LabelText("缩放行为引用")]
#endif
        [SerializeField]
        private InputActionReference zoom;

        /// <summary>
        ///  移动行为
        /// </summary>
#if ODIN_INSPECTOR
        [LabelText("移动行为引用")]
#endif
        [SerializeField]
        private InputActionReference move;

        /// <summary>
        ///  EventSystem
        /// </summary>
        [SerializeField] private EventSystem es;

        /// <summary>
        ///  相机配置
        /// </summary>
        [SerializeField]
#if ODIN_INSPECTOR
        [LabelText("相机配置")]
#endif
        private AroundWithMoveCameraConfig config;

        /// <summary>
        ///  看向点
        /// </summary>
        private Transform Target => orbital.FollowTarget;

        /// <summary>
        ///  半径
        /// </summary>
        private float _radius;

        /// <summary>
        ///  水平旋转
        /// </summary>
        private float _horizontalRotate;

        /// <summary>
        ///   垂直旋转
        /// </summary>
        private float _verticalRotate;

        /// <summary>
        ///  目标位置
        /// </summary>
        private Vector3 _position;

        /// <summary>
        ///  开始移动的方向 X
        /// </summary>
        private Vector3 _moveDirectionX;

        /// <summary>
        ///  开始移动的方向 Y
        /// </summary>
        private Vector3 _moveDirectionY;

        /// <summary>
        ///  是否获取到焦点
        /// </summary>
        private bool _hasFocus = true;

        /// <summary>
        ///  当前实例能否接受操作输入
        /// </summary>
        private bool _canOperation;

        /// <summary>
        ///  记录的初始 看向点位置、水平旋转、垂直旋转和半径
        /// </summary>
        private (Vector3 position, float horizontal, float vertical, float radius)? _record;

    
        private void Awake()
        {
            // 初始化
            Init();

            // 监听复位
            reset.action.performed += OnResetCamera;

            //监听允许移动行为
            allowMove.action.started += OnStartMove;

            // 监听结束移动行为
            allowMove.action.canceled += OnEndMove;

            // 监听允许旋转行为
            allowRotate.action.started += OnStartRotate;

            // 监听结束旋转行为
            allowRotate.action.canceled += OnEndRotate;

            // 监听缩放行为 
            zoom.action.performed += OnZoom;
        }

        private void OnDestroy()
        {
            // 取消监听复位
            reset.action.performed -= OnResetCamera;

            //取消监听允许移动行为
            allowMove.action.started -= OnStartMove;

            // 取消监听结束移动行为
            allowMove.action.canceled -= OnEndMove;

            // 取消监听允许旋转行为
            allowRotate.action.started -= OnStartRotate;

            // 取消监听结束旋转行为
            allowRotate.action.canceled -= OnEndRotate;

            // 取消监听缩放行为
            zoom.action.performed -= OnZoom;
        }

        private void OnEnable()
        {
            if (!inputAsset.enabled) inputAsset.Enable();
        }

        private void Update()
        {
            // 使用lerp进行插值位置
            Target.position = Vector3.Lerp(Target.position, _position,
                config.moveConfig.moveDamping * Time.deltaTime);

            // 使用lerp进行插值缩放
            orbital.Radius = Mathf.Lerp(orbital.Radius, _radius,
                config.zoomConfig.zoomDamping * Time.deltaTime);

            // 使用lerp进行插值旋转 X
            orbital.HorizontalAxis.Value = Mathf.Lerp(orbital.HorizontalAxis.Value,
                _horizontalRotate, config.rotateConfig.rotateDamping.x * Time.deltaTime);

            // 使用lerp进行插值旋转 Y
            orbital.VerticalAxis.Value = Mathf.Lerp(orbital.VerticalAxis.Value,
                _verticalRotate, config.rotateConfig.rotateDamping.y * Time.deltaTime);

            // 判断是否可以操作
            _canOperation = CanOperation();
        }

        /// <summary>
        ///  初始化
        /// </summary>
        private void Init()
        {
            _position = Target.position;
            _horizontalRotate = orbital.HorizontalAxis.Value;
            _verticalRotate = orbital.VerticalAxis.Value;
            _radius = orbital.Radius;

            // 记录信息
            _record = (_position, _horizontalRotate, _verticalRotate, _radius);

            // 初始化轨道
            InitOrbital();
        }

        /// <summary>
        ///  恢复相机到初始位置
        /// </summary>
#if ODIN_INSPECTOR
        [Button("恢复相机位置")]
#endif
        public void ResetCameraState(bool immediately = false)
        {
            // 开启旋转分析器，否则无法归位
            rotationComposer.enabled = true;

            if (_record.HasValue)
            {
                _position = _record.Value.position;
                _horizontalRotate = _record.Value.horizontal;
                _verticalRotate = _record.Value.vertical;
                _radius = _record.Value.radius;

                if (immediately)
                {
                    Target.position = _position;
                    orbital.HorizontalAxis.Value = _horizontalRotate;
                    orbital.VerticalAxis.Value = _verticalRotate;
                    orbital.Radius = _radius;
                }
            }
            else
            {
                Debug.Log("没有记录的初始状态，无法恢复相机位置", this);
            }
        }

        /// <summary>
        ///  复位相机
        /// </summary>
        private void OnResetCamera(InputAction.CallbackContext obj)
        {
            Debug.Log("_canOperation" + _canOperation);
            if (!_canOperation || !config.allowDoubleClickReset) return;

            ResetCameraState(true);
        }

        /// <summary>
        ///  初始化轨道
        /// </summary>
        private void InitOrbital()
        {
            orbital.HorizontalAxis.Range = config.rotateConfig.GetHorizontalLimit();
            orbital.HorizontalAxis.Wrap = config.rotateConfig.IsHorizontalWrap();

            orbital.VerticalAxis.Range = config.rotateConfig.GetVerticalLimit();
            orbital.VerticalAxis.Wrap = false;
        }

        /// <summary>
        ///  缩放行为
        /// </summary>
        private void OnZoom(InputAction.CallbackContext obj)
        {
            //如果不允许缩放，直接返回
            if (!_canOperation || !config.zoomConfig.allowZoom) return;

            // 获取鼠标滚轮的滚动值            
            var zoomDelta = obj.ReadValue<Vector2>().y;

            // 调整缩放半径
            _radius += zoomDelta * config.zoomConfig.GetZoomSpeed(orbital.Radius) * Time.deltaTime;
            _radius = config.zoomConfig.ClampZoom(_radius);
        }

        /// <summary>
        ///   结束旋转行为，设置flag
        /// </summary>
        private void OnEndRotate(InputAction.CallbackContext obj)
        {
            // 取消监听旋转行为
            move.action.performed -= OnRotating;
        }

        /// <summary>
        ///  开始旋转行为，设置flag
        /// </summary>
        private void OnStartRotate(InputAction.CallbackContext obj)
        {
            // 如果不允许旋转，直接返回
            if (!_canOperation || !config.rotateConfig.allowRotate) return;

            // 监听旋转行为
            move.action.performed += OnRotating;

            // 开启旋转时，停止移动的lerp
            _position = Target.position;

            // 开启旋转控制器
            rotationComposer.enabled = true;
        }

        /// <summary>
        ///  结束移动后，设置flag
        /// </summary>
        private void OnEndMove(InputAction.CallbackContext obj)
        {
            // 取消监听移动行为
            move.action.performed -= OnMoving;
        }

        /// <summary>
        ///  开始移动后，设置flag
        /// </summary>
        private void OnStartMove(InputAction.CallbackContext obj)
        {
            // 如果不允许移动，直接返回
            if (!_canOperation || !config.moveConfig.allowMove) return;

            // 监听移动行为
            move.action.performed += OnMoving;

            // 记录移动方向
            _moveDirectionX = orbital.transform.right;
            _moveDirectionY =
                config.moveConfig.mapping == AroundWithMoveCameraConfig.ScreenVerticalMapping.CameraForward
                    ? config.moveConfig.GetVectorProjection(orbital.transform.up + orbital.transform.forward)
                    : orbital.transform.up;

            //停止旋转lerp
            _horizontalRotate = orbital.HorizontalAxis.Value;
            _verticalRotate = orbital.VerticalAxis.Value;
            rotationComposer.enabled = false;
        }

        private void OnRotating(InputAction.CallbackContext obj)
        {
            // 获取当前帧的旋转量
            Vector2 rotateDelta = obj.ReadValue<Vector2>();

            // 获取水平旋转
            _horizontalRotate += rotateDelta.x * config.rotateConfig.rotateSpeed.x * Time.deltaTime;
            _horizontalRotate = config.rotateConfig.ClampHorizontal(_horizontalRotate);

            // 获取垂直旋转
            _verticalRotate += rotateDelta.y * config.rotateConfig.rotateSpeed.y * Time.deltaTime;
            _verticalRotate = config.rotateConfig.ClampVertical(_verticalRotate);
        }

        /// <summary>
        ///  移动行为
        /// </summary>
        private void OnMoving(InputAction.CallbackContext obj)
        {
            // 获取当前帧的移动量
            var moveDelta = obj.ReadValue<Vector2>();

            // 获取移动速度
            var moveSpeed = config.moveConfig.GetMoveSpeed(
                config.zoomConfig.GetNormalizedRadius(orbital.Radius));

            // 计算x轴和y轴的移动量
            var xMove = moveDelta.x * moveSpeed.x * Time.deltaTime
                        * _moveDirectionX;
            var yMove = moveDelta.y * moveSpeed.y * Time.deltaTime
                        * _moveDirectionY;

            // 设置临时位置
            _position = Target.position + xMove + yMove;

            // 限制临时位置
            if (!config.moveConfig.NeedLimit) return;
            _position = _position.Clamp(config.moveConfig.MinPosition, config.moveConfig.MaxPosition);
        }

        /// <summary>
        ///  能否处理操作
        /// </summary>
        private bool CanOperation()
        {
            // 当前激活的虚拟相机是否是当前相机
            var active = (CinemachineVirtualCameraBase)brain.ActiveVirtualCamera ==
                         orbital.VirtualCamera;

            if (es == null) throw new Exception("EventSystem 不能为空，请在编辑器中为脚本指定");

            // 是否悬浮在UI上
            var isOverlapUI = es.IsPointerOverGameObject();

            // 鼠标是否在屏幕上
            var isMouseInScreen = Mouse.current.position.ReadValue().x > 0
                                  && Mouse.current.position.ReadValue().y > 0
                                  && Mouse.current.position.ReadValue().x < Screen.width
                                  && Mouse.current.position.ReadValue().y < Screen.height;
          
            // 当 虚拟相机激活 && 获取到焦点 并且 没有悬浮在UI上 并且 鼠标在屏幕上时 ，允许操作
            return active && _hasFocus && !isOverlapUI && isMouseInScreen;
        }

        /// <summary>
        ///  事件函数，当应用程序获得或失去焦点时调用
        /// </summary>
        private void OnApplicationFocus(bool hasFocus)
        {
            _hasFocus = hasFocus;
        }

        private void OnValidate()
        {
#if UNITY_EDITOR
            // 检查brain
            if (!brain)
            {
                var brains = FindObjectsByType<CinemachineBrain>(FindObjectsInactive.Exclude, FindObjectsSortMode.None);
                if (brains.Length != 1)
                    Debug.LogWarning($"脚本{nameof(AroundWithMoveCamera)}无法为您自动找到{nameof(CinemachineBrain)}，" +
                                     $"未找到或找到多个{nameof(CinemachineBrain)}请手动指定",
                        this);
                else
                    brain = brains[0];
            }

            //检查相机跟随器
            if (!orbital)
            {
                orbital = GetComponent<CinemachineOrbitalFollow>();
                if (!orbital)
                    Debug.LogWarning(
                        $"脚本{nameof(AroundWithMoveCamera)}无法为您自动找到相机轨道{nameof(CinemachineOrbitalFollow)}，请手动指定",
                        this);
            }

            // 检查旋转控制器
            if (!rotationComposer)
            {
                rotationComposer = GetComponent<CinemachineRotationComposer>();
                if (!rotationComposer)
                    Debug.LogWarning(
                        $"脚本{nameof(AroundWithMoveCamera)}无法为您自动找到旋转控制器{nameof(CinemachineRotationComposer)}，请手动指定",
                        this);
            }

            // 检查输入资产
            if (!inputAsset)
            {
                var total = Resources.FindObjectsOfTypeAll<InputActionAsset>();
                var find = total.FirstOrDefault(asset => asset.name == "AroundWithMove");
                if (!find)
                    Debug.LogWarning($"脚本{nameof(AroundWithMoveCamera)}无法为您自动找到输入资产{nameof(inputAsset)}，请手动指定",
                        this);
                inputAsset = find;
            }

            //获取引用寻找目录
            foreach (var each in Resources.FindObjectsOfTypeAll<InputActionReference>())
            {
                if (!allowMove && each.name == "AroundWithMove/AllowMove") allowMove = each;
                else if (!allowRotate && each.name == "AroundWithMove/AllowRotate") allowRotate = each;
                else if (!zoom && each.name == "AroundWithMove/Zoom") zoom = each;
                else if (!move && each.name == "AroundWithMove/Move") move = each;
                else if (!reset && each.name == "AroundWithMove/Reset") reset = each;
            }

            if (!allowMove)
                Debug.LogWarning($"脚本{nameof(AroundWithMoveCamera)}无法为您自动找到输入资产{nameof(allowMove)}，请手动指定",
                    this);
            if (!allowRotate)
                Debug.LogWarning($"脚本{nameof(AroundWithMoveCamera)}无法为您自动找到输入资产{nameof(allowRotate)}，请手动指定",
                    this);
            if (!zoom)
                Debug.LogWarning($"脚本{nameof(AroundWithMoveCamera)}无法为您自动找到输入资产{nameof(zoom)}，请手动指定",
                    this);
            if (!move)
                Debug.LogWarning($"脚本{nameof(AroundWithMoveCamera)}无法为您自动找到输入资产{nameof(move)}，请手动指定",
                    this);

            if (!es) es = FindAnyObjectByType<EventSystem>(FindObjectsInactive.Include);

            //初始化轨道
            InitOrbital();
#endif
        }
    }
}